home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 388_01 / ag / 93 / winner / agag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-19  |  8.2 KB  |  449 lines

  1. /*
  2.  * ag.c            
  3.  *
  4.  * Anthony's Grep
  5.  *
  6.  * Public Domain 1992, 1993 by Anthony Howe.  All rights released.
  7.  *
  8.  * Synopsis:  ag <pattern> [files...]
  9.  */
  10.  
  11. #include <stdio.h>
  12.  
  13. #ifndef ARRAY
  14. #define ARRAY    512
  15. #endif
  16.  
  17. #define EXIT_MATCH    0
  18. #define EXIT_NOMATCH    1
  19. #define EXIT_USAGE    2
  20. #define EXIT_ERROR    3
  21.  
  22. /*
  23.  * Non-deterministic Finite-state Automaton
  24.  *
  25.  * An NFA is a directed graph whose nodes are called states.  Each
  26.  * state is either a labeled state denoting a literal value, or a
  27.  * NIL state that has no label.  Each state has at most two edges
  28.  * leaving it.  There is only one start state and one final state,
  29.  * which may be the same state.
  30.  *
  31.  * Various state-structures can be represented by an array.  These 
  32.  * structures have only one start point being the left most index,
  33.  * and one end point being the right most index.
  34.  *
  35.  *    a        ...[a]...
  36.  *
  37.  *    ab        ...[a][b]...
  38.  *
  39.  *    ^a        [bol][a]...
  40.  *
  41.  *    a$        ...[a][eol]
  42.  *
  43.  *    a.b        ...[a][wild][b]...
  44.  *                    _
  45.  *                   v \
  46.  *    a*        ...[\][a][\]...
  47.  *                 \____^
  48.  *
  49.  *    a?        ...[\][a]...
  50.  *                 \___^
  51.  *                        _______________
  52.  *                       /               v
  53.  *    a|b        ...[\][a][/][stop][pass][b]...
  54.  *                 \_____________^
  55.  *
  56.  *    [a0-9]        ...[class][a][0][1][2][3][4][5][6][7][8][9][stop]...
  57.  *
  58.  *    [^a0-9]        ...[nclass][a][0][1][2][3][4][5][6][7][8][9][stop]...
  59.  *
  60.  *                        _____________  _______________
  61.  *                       /             v/               v
  62.  *    a|b|c        ...[\][a][/][stop][\][b][/][stop][pass][c]...
  63.  *                 \_____________^\_____________^
  64.  *
  65.  *                    _            _______________
  66.  *                   v \          /               v
  67.  *    a*(b|c)        ...[\][a][\][\][b][/][stop][pass][c]...
  68.  *                 \____^   \_____________^
  69.  */
  70.  
  71. #define BOL        ('\n')
  72. #define EOL        ('\n')
  73. #define STOP        ('\n')
  74. #define WILD        (0)
  75. #define CLASS        (-1)
  76. #define NCLASS        (-2)
  77. #define PASS        (-3)
  78. #define JUMP(n)        (-4-(n))
  79. #define CHAR(c)        (c)
  80. #define ISJUMP(x)    ((x) <= PASS)
  81. #define ISCHAR(x)    (0 <= (x))
  82.  
  83. /*
  84.  * Special member that is contained in all sets.  It is the location
  85.  * of the WILD element that the pattern array is initialised with in 
  86.  * main().  Due to the way from() performs the pattern search, this 
  87.  * member is always the first member of a set and so can be used to 
  88.  * delimit sets.
  89.  */
  90. #define MEMBER        2
  91.  
  92. typedef int I;
  93. typedef char C;
  94. typedef void V;
  95.  
  96. I chr;
  97. I final;
  98. I nesting;
  99. I array[ARRAY], next;
  100.  
  101. /*
  102.  * The number of states will always be related to the number of
  103.  * unique sets in set[], which is less-than-equal-to next_set.
  104.  * There is no need to bounds check dfa[][] since next_set 
  105.  * is bounds checked in from() and would cause an error well in 
  106.  * advance of overflowing the total number of possible states.
  107.  */
  108. I dfa[ARRAY][ARRAY];
  109. I set[ARRAY], new_set, next_set;
  110.  
  111. I new();
  112. I match();
  113. I inclass();
  114. I isfinal();
  115. V expr();
  116. V term();
  117. V factor();
  118. V literal();
  119. V from();
  120.  
  121. I
  122. main(argc, argv)
  123. I argc;
  124. C **argv;
  125. {
  126.     I error;
  127.     FILE *file;
  128.     C *name, line[BUFSIZ+1];
  129.  
  130.     if (--argc < 1) {
  131.         fprintf(stderr, "usage:  ag <pattern> [files...]\n");
  132.         return (EXIT_USAGE);
  133.     }
  134.  
  135.     /* Initialise array[] = [pass][3][wild][1]... */
  136.     new(PASS);
  137.     new(JUMP(next+2));
  138.     new(WILD);
  139.     new(JUMP(next-1));
  140.  
  141.     expr(&(*++argv));
  142.  
  143.     *line = BOL;
  144.     next_set = 1;
  145.     error = EXIT_NOMATCH;
  146.     do {
  147.         name = "-";
  148.         file = stdin;
  149.         if (1 < argc && **++argv != '-') {
  150.             if (NULL == (file = fopen(name = *argv, "r"))) {
  151.                 fprintf(stderr, 
  152.                     "ag: Failed to open '%s'.\n", name);
  153.                 error = EXIT_ERROR;
  154.                 continue;
  155.             }
  156.         }
  157.         while (fgets(line+1, BUFSIZ, file) != NULL) {
  158.             if (match(line)) {
  159.                 printf("%s:%s", name, line+1);
  160.                 error = EXIT_MATCH; 
  161.             }
  162.         }
  163.         fclose(file);
  164.     } while (1 < --argc);
  165.     return (error);
  166. }
  167.  
  168. /*
  169.  *    expr
  170.  *        term
  171.  *        expr | term
  172.  */
  173. V
  174. expr(p)
  175. C **p;
  176. {
  177.     I i, j;
  178.     i = new(PASS);
  179.     term(p);
  180.     while (**p == '|') {
  181.         ++*p;
  182.         j = new(PASS);
  183.         new(STOP);
  184.         array[i] = JUMP(next);
  185.         i = new(PASS);
  186.         term(p);
  187.         array[j] = JUMP(next);
  188.     }
  189. }
  190.  
  191. /*
  192.  *    term
  193.  *        factor
  194.  *        term factor
  195.  */
  196. V
  197. term(p)
  198. C **p;
  199. {
  200.     while (**p != '|' && (0 == nesting || **p != ')')  && **p != '\0')
  201.         factor(p);
  202. }
  203.  
  204. /*
  205.  *    factor
  206.  *        ^
  207.  *        $
  208.  *        literal
  209.  *        ( expr )
  210.  *        factor *
  211.  */
  212. V
  213. factor(p)
  214. C **p;
  215. {
  216.     I i = new(PASS);
  217.     if (**p == '^') {
  218.         ++*p;
  219.         new(BOL);
  220.     } else if (**p == '$') {
  221.         ++*p;
  222.         new(EOL);
  223.     } else if (**p == '(') { 
  224.         ++*p; 
  225.         ++nesting;
  226.         expr(p); 
  227.         if (**p != ')') { 
  228.             fprintf(stderr, "ag: Missing ')'.\n"); 
  229.             exit(EXIT_ERROR); 
  230.         }
  231.         --nesting;
  232.         ++*p;
  233.     } else {
  234.         literal(p);
  235.     }
  236.     if (**p == '*') {
  237.         ++*p;
  238.         array[i] = JUMP(next);
  239.         new(JUMP(i+1));
  240.     } else if (**p == '?') {
  241.         ++*p;
  242.         array[i] = JUMP(next);
  243.     }
  244. }
  245.  
  246. /*
  247.  *    literal
  248.  *        .
  249.  *        class 
  250.  *        character
  251.  *        \ character
  252.  *
  253.  *    class
  254.  *        [ ] ]
  255.  *        [ range ]
  256.  *        [ ] range ]
  257.  *        [ ^ range ]
  258.  *        [ ^ ] range ]
  259.  *
  260.  *    range
  261.  *        - 
  262.  *        character
  263.  *        range character
  264.  *        character_low - character_hi
  265.  *        range character_low - character_hi
  266.  */
  267. V
  268. literal(p)
  269. C **p;
  270. {
  271.     I i, j;
  272.     if (**p == '.') {
  273.         new(WILD);
  274.     } else if (**p == '[') {
  275.         ++*p;
  276.         i = new(CLASS);
  277.         if (**p == '^') {
  278.             ++*p;
  279.             array[i] = NCLASS;
  280.         }
  281.         if (**p == ']') {
  282.             ++*p;
  283.             new(CHAR(']'));
  284.         }
  285.         while ((i = **p) != ']') {
  286.             if ((*p)[1] == '-' && i < (j = (*p)[2])) {
  287.                 while (i <= j)
  288.                     new(CHAR(i++));
  289.                 *p += 3;
  290.             } else {
  291.                 new(CHAR(i));
  292.                 ++*p;
  293.             }
  294.         }
  295.         new(STOP);
  296.     } else {
  297.         if (**p == '\\')
  298.             ++*p;
  299.         new(CHAR(**p));
  300.     }
  301.     ++*p;
  302. }
  303.  
  304. /*
  305.  * Allocate an element from the pool of free elements.  The
  306.  * global variable 'next' points to the next free element in
  307.  * the pool.  Return the allocated element's index.
  308.  */
  309. I
  310. new(c)
  311. I c;
  312. {
  313.     if (ARRAY <= next) {
  314.         fprintf(stderr, "ag: Pattern too long.\n");
  315.         exit(EXIT_ERROR);
  316.     }
  317.     array[next] = c;
  318.     return (next++);
  319. }
  320.  
  321. /*
  322.  * Find all labeled elements reachable from element 'p' and add to 
  323.  * the global set 'new_set' those that are labeled with global 'chr'.  
  324.  * 'final' is true if the final state can be reached.
  325.  */
  326. V
  327. from(p)
  328. I p;
  329. {
  330.     I i = new_set;
  331.     if (next <= p)
  332.         return;
  333.     if (ISJUMP(array[p])) {
  334.         if (array[p] != PASS)
  335.             from(JUMP(array[p]));
  336.         from(p+1);
  337.     } else if (WILD == array[p] || array[p] == CHAR(chr) || inclass(&p)) {
  338.         /* If element 'p' already a member of set 't' then return. */
  339.         while (i < next_set)
  340.             if (set[i++] == p)
  341.                 return;
  342.         /* Can we reach the final state from here? */
  343.         final = isfinal(p+1);
  344.  
  345.         if (ARRAY <= next_set) {
  346.             fprintf(stderr, "ag: Out of space.\n");
  347.             exit(EXIT_ERROR);
  348.         }
  349.         /* Add element 'p' to set 't'. */
  350.         set[next_set++] = p;
  351.     }
  352. }
  353.  
  354. /*
  355.  * If element pointed to by 'p' is CLASS, return true if 'c' is in 
  356.  * the class.  If the element is NCLASS, return true if 'c' is not
  357.  * in the class.  Pass back the end of the character class in 'p'.
  358.  */
  359. I
  360. inclass(p)
  361. I *p;
  362. {
  363.     I i = 0, j = 0;
  364.     if (array[*p] == CLASS || (j = array[*p] == NCLASS)) {
  365.         while (array[++*p] != STOP)
  366.             if (array[*p] == CHAR(chr))
  367.                 i = 1;
  368.     }
  369.     return (i ^ j);
  370. }
  371.  
  372. /*
  373.  * Return true if the end point (final state) can be reached
  374.  * from element 'p' without crossing any labeled elements.
  375.  */
  376. I
  377. isfinal(p)
  378. I p;
  379. {
  380.     return (
  381.         final || p == next || (
  382.             ISJUMP(array[p]) && (
  383.                 (array[p] != PASS && isfinal(JUMP(array[p]))) 
  384.                 || isfinal(p+1)
  385.             )
  386.         )
  387.     );
  388. }
  389.  
  390. /*
  391.  * Return true if the given string matches.  The search string must 
  392.  * be of the form "\ntext\n\0" in order to handle line anchoring.
  393.  */
  394. I
  395. match(p)
  396. C *p;
  397. {
  398.     I state, i, j, k;
  399.     final = state = 0;
  400.     while (*p != '\0' && 0 <= state) {
  401.         if (0 < (i = dfa[state][*p])) {
  402.             /* Transition on known DFA state. */
  403.             state = i;
  404.         } else {
  405.             /* Perform lazy DFA evaluation from NFA.
  406.              *
  407.              * Compute new_set, which represents a transition
  408.              * from the current state's set on character *p. 
  409.              * dfa[state]['\0'] contains the set[] index for 
  410.              * the current state.
  411.              */
  412.             chr = *p;
  413.             i = dfa[state]['\0'];
  414.             j = new_set = next_set;
  415.             do 
  416.                 from(set[i++]+1);
  417.             while (set[i] != MEMBER);
  418.  
  419.             /* Determine if this is a known set or not. */
  420.             i = k = 0;
  421.             while (i < new_set) {
  422.                 if (set[i] == MEMBER) {
  423.                     j = new_set;
  424.                     ++k;
  425.                 }
  426.                 if (set[i++] == set[j]) {
  427.                     if (next_set <= ++j) {
  428.                         j = new_set;
  429.                         if (set[i] == MEMBER) {
  430.                             /* Known set found. */
  431.                             next_set = new_set;
  432.                             break;
  433.                         }
  434.                     }
  435.                 } else {
  436.                     j = new_set;
  437.                 }
  438.             }
  439.             if (new_set < next_set) 
  440.                 /* New set found. */
  441.                 dfa[++k]['\0'] = new_set;
  442.             state = dfa[state][*p] = final ? -1 : k;
  443.         }
  444.         ++p;
  445.     }
  446.     return (state < 0);
  447. }
  448.  
  449.